<?php

// @formatter:off
/**
 * @file WCHolder.abs.php
 * @author Alejandro Dario Simi
 * @date $Date: 2014-06-03 03:40:32 +0000 (Tue, 03 Jun 2014) $
 *
 * $Id: WCHolder.abs.php 112 2014-06-03 03:40:32Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/trunk/includes/holders/WCHolder.abs.php $
 */
// @formatter:on

/**
 * @class WCHolder This is the specification for a generic holder. It holds the
 * logic to manage list of object, preferable WCGTableRepresentation objects
 * indexed by name and id.
 *
 * @warning Every object called item should two basic properties:
 *  - id
 *  - name
 */
abstract class WCHolder extends WCSingleton {
	/**
	 * @var string This is the class name of the items to be held.
	 */
	protected $_PropertiesItemClass = "";
	/**
	 * @var string This is the fields' prefix in the table where every held
	 * item exists.
	 * It is similar to WCGTableRepresentation::$_PropertiesPrefix
	 */
	protected $_PropertiesPrefix = "";
	/**
	 * @var string This is the table where every held item exists.
	 * It is similar to WCGTableRepresentation::$_PropertiesTable
	 */
	protected $_PropertiesTable = "";
	/**
	 * @var string This variable holds the name of the column of represented
	 * items' name.
	 * By default, it is 'name'.
	 */
	protected $_PropertiesNameColumn = "name";
	/**
	 * @var boolean This flag indicates if names may be repeated.
	 */
	protected $_PropertiesDupNames = false;
	/**
	 * @var WCConnection Pointer to the global connection object.
	 */
	protected $_db = null;
	/**
	 * @var string Shortcut to database tables prefixes.
	 */
	protected $_dbprefix = "";
	/**
	 * Constructor is protected because all holders must be singletons.
	 */
	protected function __construct() {
		WCHoldersBus::AddHolder($this);
		//
		// Loading shortcuts.
		// @{
		$this->_db = WCConnection::Instance();
		$this->_dbprefix = $this->_db->prefix();
		// @}
	}
	/**
	 * Class destructor.
	 */
	public function __destruct() {
		
	}
	//
	// Public methods.
	/**
	 * This method allows to create a new item in the database. It also adds
	 * it to the internal list.
	 *
	 * @param string $name Name of the new item to be created.
	 * @return WCRepresentation On success returns the new object.
	 * Otherwise returns a null pointer.
	 */
	public function create($name) {
		//
		// Loading global pointers.
		global $wcDefaults;
		//
		// This is the default value in case the item cannot be created.
		$out = false;
		//
		// Checking that Read Only Mode is not activated.
		if(!$wcDefaults["read-only"]) {
			//
			// Checking if there's no other item with the same name.
			// Unless duplicates are allowed.
			if($this->_PropertiesDupNames || count($this->idsByName($name)) == 0) {
				//
				// Creating the statement to add an item.
				// @{
				$stmt = null;
				$stmtId = __CLASS__."::".__FUNCTION__."[{$this->_PropertiesTable}]";
				if($this->_db->has($stmtId)) {
					$stmt = $this->_db->get($stmtId);
				} else {
					$query = "insert\n";
					$query .= "        into {$this->_dbprefix}{$this->_PropertiesTable} (\n";
					$query .= "                {$this->_PropertiesPrefix}{$this->_PropertiesNameColumn})\n";
					$query .= "        values (:name)";

					$stmt = $this->_db->prepare($stmtId, $query);
				}
				// @}
				//
				// Inserting the new item into the database.
				$params = array(":name" => $name);
				if($stmt->execute($params)) {
					//
					// If there were no issues creating the
					// item, it is add to the list and
					// returned.
					$out = $this->item($this->_db->lastInsertId());
				}
			}
		}
		//
		// Returning the results of the creation.
		return $out;
	}
	/**
	 * This method return the item with the specified id.
	 *
	 * @param int $id Id of the item to be returned.
	 * @return WCRepresentation On success returns the requested object.
	 * Otherwise, it returns false.
	 */
	public function item($id) {
		//
		// Creating a new not to load the item.
		$item = new $this->_PropertiesItemClass();
		//
		// Loading the item.
		if(!$item->load($id)) {
			//
			// Cleaning used memory on failures.
			unset($item);
			$item = false;
		}

		return $item;
	}
	/**
	 * This method return the item with the specified name.
	 *
	 * @param string $name Name of the item to be returned.
	 * @return WCRepresentation On success returns the requested object.
	 * Otherwise, it returns false.
	 *
	 * @todo Bug: when this->_PropertiesDupNames is true, it may fail.
	 */
	public function itemByName($name) {
		//
		// Setting default value to be returned.
		$out = array();
		//
		// Generating the list of items.
		foreach($this->idsByName($name) as $id) {
			$item = $this->item($id);
			if($item) {
				$out[] = $item;
			}
		}
		//
		// Returning found item.
		return $out;
	}
	public function itemByNameCI($name) {
		//
		// Setting default value to be returned.
		$out = array();
		//
		// Generating the list of items.
		foreach($this->idsByName($name, false) as $id) {
			$item = $this->item($id);
			if($item) {
				$out[] = $item;
			}
		}
		//
		// Returning found item.
		return $out;
	}
	/**
	 * This method returns the list of available ids.
	 *
	 * @return int[] Returns an array of ids.
	 */
	public function itemIds() {
		//
		// By default, it returns an empty array.
		$out = array();
		//
		// Preparing the statement to load all ids.
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__."[{$this->_PropertiesTable}]";
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  {$this->_PropertiesPrefix}id as id\n";
			$query .= "from    {$this->_dbprefix}{$this->_PropertiesTable}\n";
//			$query .= "order by {$this->_PropertiesPrefix}{$this->_PropertiesNameColumn} asc\n";
			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		//
		// Asking for the ids.
		if($stmt->execute()) {
			//
			// Generating the list of items with the database
			// results.
			foreach($stmt->fetchAll() as $row) {
				$out[] = $row["id"];
			}
		}

		return $out;
	}
	/**
	 * This method return the full list of items, all of them fully loaded.
	 *
	 * @return WCRepresentation[] Returns an array with all items.
	 */
	public function items() {
		$out = array();
		//
		// Loading one by one every item.
		foreach($this->itemIds() as $id) {
			$item = $this->item($id);
			if($item) {
				$out[] = $item;
			}
		}
		//
		// Returning the list of items.
		return $out;
	}
	public function idsByName($name, $caseSensitive = true) {
		//
		// Setting default value to be returned.
		$out = array();
		//
		// Preparing the statement to load items.
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__."[{$this->_PropertiesTable}".($caseSensitive ? "" : "-CI")."]";
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  {$this->_PropertiesPrefix}id as id\n";
			$query.= "from    {$this->_dbprefix}{$this->_PropertiesTable}\n";
			if($caseSensitive) {
				$query.= "where   {$this->_PropertiesPrefix}{$this->_PropertiesNameColumn} = :name\n";
			} else {
				$query.= "where   lower({$this->_PropertiesPrefix}{$this->_PropertiesNameColumn}) = lower(:name)\n";
			}
			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		$params = array(
			":name" => $name
		);
		//
		// Asking for the ids.
		if($stmt->execute($params) && $stmt->rowCount() > 0) {
			//
			// Generating the list of items from database
			// results.
			foreach($stmt->fetchAll() as $row) {
				$out[] = $row["id"];
			}
		}
		//
		//
		return $out;
	}
	/**
	 * @obsolete
	 * 
	 * This method resets internal list forcing this holder to to reload
	 * everything.
	 */
	public function resetCache() {
		
	}
	//
	// Protected methods.
}
